package org.bridgen.runtime.inspect;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.util.HashMap;
import java.util.LinkedList;

import javax.swing.JComponent;

import org.bridgen.runtime.RecoveryHelper;
import org.bridgen.runtime.model.EOF;
import org.bridgen.runtime.model.Island;
import org.bridgen.runtime.model.LexicalNode;
import org.bridgen.runtime.model.Reef;
import org.bridgen.runtime.model.SOF;
import org.bridgen.runtime.model.Water;
import org.bridgen.runtime.util.Interval;

/**
 * 
 * @author Emma Söderberg <emma add cs.lth.se>
 *
 */

public class RecoveryTextPanel extends JComponent {
	private static final long serialVersionUID = 1L;
	private String content;
	private SOF sof;
	
	private HashMap<LexicalNode, LinkedList<Point>> nodePointMap;
	
	public RecoveryTextPanel() {
		setPreferredSize(new Dimension((WIDTH+20)*SQUARE_WIDTH,
				(HEIGHT+30)*SQUARE_HEIGHT));
		
		nodePointMap = new HashMap<LexicalNode, LinkedList<Point>>();
	}
	
	public void showInitResult(String content) {
		this.content = content;
		sof = null;
		offsetToPos = new Point[content.length()];
		linkOffsetAndPoint();
		setPreferredSize(new Dimension(WIDTH*SQUARE_WIDTH,
				HEIGHT*SQUARE_HEIGHT));
		revalidate();
		repaint();
	}
	
	public void showLexerResult(SOF sof) {
		this.sof = sof;
		repaint();
	}
	
		public void showLinkResult() {
		repaint();
	}
	
	public void showRecoverResult() {
		repaint();
	}
	
	public void showOutputResult(String content) {
		SOF sof = this.sof;
		showInitResult(content);
		this.sof = sof;
		repaint();
	}
	
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		Font font = new Font("Serif", Font.PLAIN, 12);
		g.setFont(font);
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, WIDTH*SQUARE_WIDTH + XMARGIN, HEIGHT*SQUARE_HEIGHT);

		if (content == null && sof == null) {
			paintGrid(g);
		} else {
			if (sof != null) {
				paintNodes(g);
			}
			paintText(g);
			paintGrid(g);
			if (sof != null) {
				paintBridges(g);
			}
		} 
	}

	private int WIDTH = 30;
	private int HEIGHT = 20;
	private static final int SQUARE_WIDTH = 15;
	private static final int SQUARE_XPAD = 3;
	private static final int BRIDGE_XPAD = 7;
	private static final int SQUARE_HEIGHT = 17;
	private static final int SQUARE_YPAD = 13;
	private static final int BRIDGE_YPAD = 8;
	private static final int XMARGIN = 25;
	private final Color gridColor = new Color(231,228,228);
	private final Color textColor = new Color(128,128,128);
	private final Color waterColor = new Color(189,231,246);
	private final Color islandColor = new Color(180,224,169);
	private final Color reefColor = new Color(163,212,201);
	private final Color limitColor = Color.orange;

	private void paintGrid(Graphics g) {
		int line = 1;
		for (int y = 0; y < HEIGHT*SQUARE_HEIGHT; y+=SQUARE_HEIGHT) {
			g.setColor(textColor);
			g.drawString(String.valueOf(line++), SQUARE_XPAD, y + SQUARE_YPAD);
			g.setColor(gridColor);
			for (int x = 0; x < WIDTH*SQUARE_WIDTH; x+=SQUARE_WIDTH) {
				g.drawRect(XMARGIN + x,y,SQUARE_WIDTH,SQUARE_HEIGHT);
			}
		}
		// Draw limit line to indicate where real offsets start
		//g.setColor(limitColor);
		//g.drawLine(XMARGIN + SQUARE_WIDTH, 0, 
			//	XMARGIN + SQUARE_WIDTH, HEIGHT*SQUARE_HEIGHT);
	}
	/*
		private void paintNodeList(Graphics g) {
			LexicalNode node = sof.getNext();
			int index = 0; 
			int xOffset = WIDTH*SQUARE_WIDTH + XMARGIN + SQUARE_WIDTH;
			int yOffset = 0;
			while (!(node instanceof EOF)) {
				if (node instanceof Water && showWater) {
					g.setColor(waterColor);
				} else if (node instanceof Island && showIslands) {
					if (showBridges && ((Island)node).isFake()) {
						g.setColor(Color.RED);
						g.drawRect(xOffset, yOffset + index*SQUARE_HEIGHT, SQUARE_WIDTH, SQUARE_HEIGHT);
					}
					g.setColor(islandColor);
				} else if (node instanceof Reef && showReefs) {
					g.setColor(reefColor);
				} else {
					g.setColor(Color.WHITE);
				}
				g.fillRect(xOffset, yOffset + index*SQUARE_HEIGHT, SQUARE_WIDTH, SQUARE_HEIGHT);
				g.setColor(textColor);
				g.drawString(node.getInterval().toString(), 
					xOffset + SQUARE_WIDTH + SQUARE_XPAD, yOffset + index*SQUARE_HEIGHT + SQUARE_YPAD);
				g.setColor(gridColor);
				if (showBridges && node instanceof Island && ((Island)node).isFake()) {
					g.setColor(Color.RED);
				}
				g.drawRect(xOffset, yOffset + index*SQUARE_HEIGHT, SQUARE_WIDTH, SQUARE_HEIGHT);
				index++;
				node = node.getNext();
			}
		}
	 */
	/*
		private void paintRecoveryScore(Graphics g) {
			if (showRecoveryScore) {
				int width = WIDTH*SQUARE_WIDTH + XMARGIN;
				int height = 5*SQUARE_HEIGHT;
				int xOffset = 0;
				int yOffset = HEIGHT*SQUARE_HEIGHT;
				g.setColor(Color.WHITE);
				g.fillRect(xOffset,yOffset,width,height);
				g.setColor(textColor);
				g.drawString("Recovery score: " + 
					String.valueOf(recoveryScore) + " %", 
					xOffset + SQUARE_XPAD, 
					yOffset + SQUARE_HEIGHT + SQUARE_YPAD);
			}
		}
	 */

	//private ArrayList<Interval> lineIntervals = new ArrayList<Interval>();
	private Point[] offsetToPos;
	
	private void paintText(Graphics g) {
		char[] c = content.toCharArray();
		g.setColor(textColor);
		for (int i = 0; i < c.length; i++) {
			g.drawChars(c, i, 1, 
					(int)offsetToPos[i].getX()*SQUARE_WIDTH + SQUARE_XPAD + XMARGIN,
					(int)offsetToPos[i].getY()*SQUARE_HEIGHT + SQUARE_YPAD);
		}
	}
	
	private Point sofPoint;
	private Point eofPoint;
	
	private void linkOffsetAndPoint() {
		char[] c = content.toCharArray();
		int line = 0;
		int lineOffset = 1;
		int maxCols = 0;
		for (int i = 0; i < c.length; i++) {
			if (c[i] == '\n') {
				int cols = i - lineOffset;
				if (cols > maxCols) {
					maxCols = cols;
				}
				offsetToPos[i] = new Point(cols, line);
				line++;
				lineOffset = i + 1;
			} else {
				offsetToPos[i] = new Point(i-lineOffset, line);
			}
		}
		sofPoint = new Point(0, 0);
		eofPoint = new Point(maxCols + 1, line);
		WIDTH = maxCols + 4;
		HEIGHT = line + 4;
	}

	private void paintNodes(Graphics g) {
		LexicalNode node = sof.getNext();
		Interval interval = node.getInterval();
		int startOffset = interval.getStart();
		int endOffset = interval.getEnd();
		boolean paint = false;
		while (node != null) {
			/* DEBUG System.out.println("Checking node: " + node); */
			
			// Don't paint the SOF and EOF node
			if (node instanceof SOF || node instanceof EOF) {
				paint = false;
			}
			
			if (node instanceof Water) {
				g.setColor(waterColor);
				paint = true;
			} else if (node instanceof Island) {
				g.setColor(islandColor);
				paint = true;
			} else if (node instanceof Reef) {
				g.setColor(reefColor);
				paint = true;
			}
			if (paint) {
				if (interval instanceof Interval.Start) {
					g.fillRect((int)sofPoint.getX()*SQUARE_WIDTH + XMARGIN, 
							(int)sofPoint.getY()*SQUARE_HEIGHT, SQUARE_WIDTH, SQUARE_HEIGHT);
					g.setColor(limitColor);
					g.fillRect((int)sofPoint.getX()*SQUARE_WIDTH + XMARGIN, 
							(int)sofPoint.getY()*SQUARE_HEIGHT, SQUARE_WIDTH, SQUARE_HEIGHT/2);
				} else if (interval instanceof Interval.End) {
					g.fillRect((int)eofPoint.getX()*SQUARE_WIDTH + XMARGIN, 
							(int)eofPoint.getY()*SQUARE_HEIGHT, SQUARE_WIDTH, SQUARE_HEIGHT);
					g.setColor(limitColor);
					g.fillRect((int)eofPoint.getX()*SQUARE_WIDTH + XMARGIN, 
							(int)eofPoint.getY()*SQUARE_HEIGHT, SQUARE_WIDTH, SQUARE_HEIGHT/2);
				} else {
					g.fillRect((int)offsetToPos[startOffset].getX()*SQUARE_WIDTH + XMARGIN, 
						(int)offsetToPos[startOffset].getY()*SQUARE_HEIGHT, SQUARE_WIDTH, SQUARE_HEIGHT);
				}
				paint = false;
			}
			if (++startOffset >= endOffset) {
				node = node.getNext();
				if (node != null) {
					interval = node.getInterval();
					startOffset = interval.getStart();
					endOffset = interval.getEnd();
				}
			}
		} 
	}

	private void paintBridges(Graphics g) {
		Island island = RecoveryHelper.nextIsland(sof);
		while (!(island instanceof EOF)) {
			if (island.isFake()) {
				g.setColor(Color.RED);
				Point start = offsetToPos[island.getInterval().getStart()];
				int startx = (int)start.getX()*SQUARE_WIDTH + XMARGIN;
				int starty = (int)start.getY()*SQUARE_HEIGHT;
				g.drawLine(startx, starty, startx, starty + SQUARE_HEIGHT);
			} else if (!island.hasBridge()) {
				g.setColor(Color.RED);
				Point start = offsetToPos[island.getInterval().getStart()];
				g.drawRect((int)start.getX()*SQUARE_WIDTH + XMARGIN, (int)start.getY()*SQUARE_HEIGHT, 
						SQUARE_WIDTH, SQUARE_HEIGHT);		
			} else {
				g.setColor(Color.BLACK);
				Point start = offsetToPos[island.getInterval().getStart()];
				g.drawRect((int)start.getX()*SQUARE_WIDTH + XMARGIN, (int)start.getY()*SQUARE_HEIGHT, 
						SQUARE_WIDTH, SQUARE_HEIGHT);		
			}

			if (island.startOfBridge() && island.hasBridge()) {
				Point start = offsetToPos[island.getInterval().getStart()];
				Island otherSide = island.getBridge().getEnd();
				Point end = offsetToPos[otherSide.getInterval().getStart()];
				if (island.isFake() || otherSide.isFake()) {
					g.setColor(Color.RED);
				} else {
					g.setColor(Color.BLACK);
				}
				g.drawLine((int)start.getX()*SQUARE_WIDTH + XMARGIN + BRIDGE_XPAD, 
						(int)start.getY()*SQUARE_HEIGHT + BRIDGE_YPAD, 
						(int)end.getX()*SQUARE_WIDTH + XMARGIN + BRIDGE_XPAD, 
						(int)end.getY()*SQUARE_HEIGHT + BRIDGE_YPAD);	
			}
			island = RecoveryHelper.nextIsland(island);
		} 
	}

	public String getStatusForPoint(Point p) {
		int x = (int)p.getX();
		int y = (int)p.getY();
		int row = y/SQUARE_HEIGHT;
		int col = (x-XMARGIN)/SQUARE_WIDTH;
		String msg = "x:" + x + " y: " + y + " row = " + (row + 1) + " col: " + (col + 1);
		if (sof != null) {
			LexicalNode prev = null;
			LexicalNode node = sof.getNext();
			while (!(node instanceof EOF)) {
				Interval interval = node.getInterval();
				Point point = sofPoint;
				if (interval instanceof Interval.End) {
					point = eofPoint;
				} else {
					point= offsetToPos[interval.getStart()];
				}
				if (point.getY() == row && point.getX() > col) {
					break;
				}
				prev = node;
				node = node.getNext();
			}
			if (!(node instanceof EOF) && prev != null) {
				msg = prev.toString();
			}
		}
		return msg;
	}
}
